/*
 * 易族智汇（北京）科技有限公司 版权所有。
 * 未经许可，您不得使用此文件。
 * 官方地址：www.javamall.com.cn
 */
package com.enation.app.javashop.framework.security.sign;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.enation.app.javashop.framework.JavashopConfig;
import com.enation.app.javashop.framework.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.enation.app.javashop.framework.util.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ReflectionUtils;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;

/**
 * 签名工具
 * @author kingapex
 * @version 1.0
 * @data 2021/11/20 11:28
 **/
public class SignUtil {

    private static  final Logger logger = LoggerFactory.getLogger(SignUtil.class);

    public static Boolean isSign = false;

    /**
     * 签名秘钥
     * 注意：此秘钥修改需要谨慎操作，如果秘钥已经被使用过，根据此秘钥生成的数据将无法正确验签！！！
     */
    private static final String SECRET = "d7beb523cd3437245c48a8659865";

    /**
     * 检查某个对象中签名字段存在，且有值，也就是会更新签名字段
     * @param entity
     * @return
     */
    public static boolean signFieldExisted(Object entity) {
        Field[] fields = entity.getClass().getDeclaredFields();
        long count =Arrays.stream(fields).filter(field -> {

            if (field.getAnnotation(SignField.class)!=null){
                ReflectionUtils.makeAccessible(field);
                Object valueObject = ReflectionUtils.getField(field, entity);
                return valueObject!=null;
            }else {
                return false;
            }

        }).count();

        return count>0;
    }




    /**
     * 对一个数据库DO实体验签
     * @param entity  数据库DO实体
     * @return 验签结果
     * @throws IllegalAccessException
     */
    public static boolean checkSign(Object entity)   {

        if(!isSign){
            return true;
        }

        try {
            Class<?> clz = entity.getClass();
            String sign = SignUtil.createSign(entity);
            Field signField = findField(clz, Sign.class);
            if (signField != null) {
                ReflectionUtils.makeAccessible(signField);

                Object valueObj = signField.get(entity);
                return sign.equals(valueObj);
            } else {
                return true;
            }
        }catch (IllegalAccessException e){
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 生成签名
     * @param entity 需要签名的对象DO
     */
    public static String createSign(Object entity) {
        Class<?> clz = entity.getClass();
        Map<String, Object> params = new HashMap<>();

        //循环每个字段，查找加密字段
        ReflectionUtils.doWithFields(clz, field -> {

            SignField signField = field.getAnnotation(SignField.class);

            if (signField != null) {
                //只有简单类型字段才参与签名
                boolean isSimpleFiled = BeanUtils.isSimpleProperty(field.getType());
                if (isSimpleFiled) {
                    ReflectionUtils.makeAccessible(field);
                    Object valueObject = field.get(entity);

                    //空值不参与签名
                    if (valueObject != null) {
                        params.put(field.getName(), valueObject.toString());
                    }


                }
            }
        });

        //生成签名
        String sign = sign(params);

        return sign;
    }


    /**
     * 根据参数生成签名
     *
     * @param params 需要参与签名的参数
     * @return
     */
    public static  String sign(Map<String, Object> params) {

        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        StringBuffer prestr = new StringBuffer();

        for (int i = 0; i < keys.size(); i++) {

            String key = keys.get(i);
            Object valObj = params.get(key);
            //跳过空值对象
            if (valObj == null) {
                continue;
            }
            String value = valObj.toString();

            if ("sign".equals(key)) {
                continue;
            }

            // 拼接时，不包括最后一个&字符
            if ("".equals(prestr.toString())) {
                prestr.append(key + "=" + value);
            } else {
                prestr.append("&" + key + "=" + value);
            }
        }

        prestr.append("&key=" + SECRET);
        String sign = StringUtil.md5(prestr.toString());
        return sign;
    }


    /**
     * 根据参数生成签名
     *
     * @param params 需要参与签名的参数
     * @return
     */
    public static  String sign(Map<String, Object> params,String nonce, String timestamp, String accessToken ) {

        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        StringBuffer prestr = new StringBuffer();

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            Object valObj = params.get(key);
            //跳过空值对象
            if (valObj == null) {
                continue;
            }
            String value = valObj.toString();
            if ("sign".equals(key)) {
                continue;
            }
            try {
                //值不为空，参与签名
                if(!StrUtil.isEmpty(value) && !"null".equals(value)){
                    prestr.append(key + "=" + value+"&");
                }
            } catch (Exception e) {
                throw new ServiceException("500", "转码错误");
            }
        }

        prestr.append("nonce="+nonce);
        prestr.append("&timestamp="+timestamp);
        prestr.append("&accessToken="+accessToken);
        logger.debug("============签名参数==========",prestr);
        String signStr =null;
        try {
            signStr = URLEncoder.encode(prestr.toString(), CharsetUtil.UTF_8).replace("+", "%20");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        logger.debug("============签名参数==========");
        logger.debug(signStr);

        String sign = StringUtil.md5(signStr);
        return sign;
    }


    /**
     * 根据注解找到相应的字段
     * 当前对象中，有且只有一个字段有此注解
     *
     * @param clz
     * @param annotationClass
     * @return
     */
    public static Field findField(Class<?> clz, Class annotationClass) {
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(annotationClass) != null) {
                return field;
            }
        }

        return null;
    }


    public static void main(String[] args) throws UnsupportedEncodingException {
        String value = "let me try";
        value = URLEncoder.encode(value, CharsetUtil.UTF_8);

        

    }
}
